16. PΩtle


    PΩtle s▒ bardzo u┐ytecznym elementem jΩzyka JavaScript (innych r≤wnie┐ :). Pozwalaj▒ one na wykonywanie zawartych w nich polece± wielokrotnie, przez co zmniejsza siΩ objΩto╢µ kodu skryptu, a zarazem oszczΩdza pracy programi╢cie. W jΩzyku JavaScript (w wersji 5.5) istniej▒ 3 rodzaje pΩtli oraz stosowanie tzw. etykietek, kt≤re nie s▒ uznawane za iteracjΩ (pΩtlΩ), jednak┐e ich u┐ywanie g│≤wnie do takiej funkcji siΩ sprowadza.

Pierwsz▒ pΩtl▒ jest pΩtla for. Jej sk│adnia wygl▒da nastΩpuj▒co:

for ([warto╢µ pocz▒tkowa licznika]; [warunek]; [zmiana licznika])
 {
  ... polecenia;
  ... polecenia;
 }

WystΩpuje tutaj taka zmienna, jak licznik pΩtli. Jego warto╢µ pocz▒tkow▒ ustawiamy w polu warto╢µ pocz▒tkowa. Zazwyczaj identyfikuje on liczbΩ przebieg≤w pΩtli, zwiΩkszan▒ w miejscu zmiana licznika. Licznik mo┐e byµ r≤wnie┐ dekrementowany (zmniejszany) i wtedy pΩtla wykonuje siΩ jakby od ko±ca do pocz▒tku.

Struktura dzia│ania pΩtli wygl▒da nastΩpuj▒co:

  1. Inicjowana jest warto╢µ pocz▒tkowa licznika
  2. Sprawdzany jest warunek. Je┐eli wynosi on true to pΩtla bΩdzie chodziµ dalej, a je┐eli wynosi false - pΩtla zostaje przerwana
  3. NastΩpuje zmiana licznika
  4. Wykonuj▒ siΩ wszystkie polecenia znajduj▒ce siΩ w bloku, po czym pΩtla przechodzi do kroku drugiego

Mo┐liwe jest zagnie┐d┐anie jednej pΩtli w drugiej, to znaczy umieszczenie jednej pΩtli w bloku drugiej. Jest to czΩsto wykorzystywane przy obs│udze wielowymiarowych tablic.

Oto przyk│ad wykorzystania pΩtli for:

<SCRIPT LANGUAGE="JavaScript">
<!--

 function potega()
  {
   var wynik = 1;
   l = parseInt(document.forms["matma"].podstawa.value);
   w = parseInt(document.forms["matma"].wykladnik.value);
   for (i = 1; i <= w; i++)
    {
     wynik *= l;
    }
   window.alert("Wynik to: " + wynik);
  }

//-->
</SCRIPT>

.. i jeszcze potrzebne elementy formularza:

<FORM name="matma">
 <INPUT TYPE="text" VALUE=2 SIZE=2 name="podstawa"> do potΩgi
 <INPUT TYPE="text" VALUE=3 SIZE=2 name="wykladnik">
 <INPUT TYPE="button" VALUE="Oblicz" onClick="potega()">
</FORM>

Oraz efekt dzia│ania:

do potΩgi

Kolejna pΩtla do..while r≤┐ni siΩ tym od poprzedniej, ┐e nie posiada ona licznika pΩtli. Podobnie zreszt▒ pΩtla while. Niekt≤rzy uwa┐aj▒ obie powy┐sze jako jedn▒ i t▒ sam▒. Ze wzglΩdu na zasadnicz▒ r≤┐nicΩ w dzia│aniu (mimo podobnej sk│adni i u┐ytych s│≤w kluczowych), wiele os≤b rozr≤┐nia je jako dwie osobne. R≤┐nica polega na tym, ┐e w pierwszym przypadku pΩtla sprawdza warunek po wykonaniu siΩ instrukcji, natomiast w drugim przypadku - przed. Obie pΩtle wykonuj▒ siΩ tak d│ugo, jak d│ugo podany warunek jest prawdziwy (ma warto╢µ logiczn▒ true).

Sk│adnia pΩtli do...while:

do
 {
  ... polecenie;
  ... polecenie;
 }
while (warunek);

Przepiszmy funkcjΩ potega() u┐ywaj▒c tej w│a╢nie pΩtli:

function potega2()
 {
  var wynik = 1;
  l = parseInt(document.forms["matma"].podstawa.value);
  w = parseInt(document.forms["matma"].wykladnik.value);
  i = 1
  do
   {
    wynik *= l;
    i++;
   }
  while (i <= w);
  window.alert("Wynik to: " + wynik);
 }

Widzimy, ┐e mimo wszystko musieli╢my zadeklarowaµ sobie sw≤j licznik pΩtli, zatem zauwa┐amy, ┐e wyb≤r odpowiedniej pΩtli zale┐y od algorytmu. Nie jest oczywi╢cie b│Ωdem, stosowanie ca│y czas jednej pΩtli, jednak stosowanie r≤┐nych pΩtli w r≤┐nych sytuacjach na pewno u│atwi nam ┐ycie.

Tak wygl▒da sk│adnia pΩtli while:

while (warunek)
 {
  ... polecenie;
  ... polecenie;
 }

A funkcja potega() przepisana z u┐yciem pΩtli while:

function potega3()
 {
  var wynik = 1;
  l = parseInt(document.forms["matma"].podstawa.value);
  w = parseInt(document.forms["matma"].wykladnik.value);
  i = 1
  while (i <= w)
   {
    wynik *= l;
    i++;
   }
  window.alert("Wynik to: " + wynik);
 }

W tym przypadku nie widaµ r≤┐nicy miΩdzy obiema pΩtlami, poniewa┐ wykonywane polecenia nie zale┐▒ od licznika pΩtli. Jednak na przyk│ad przy u┐yciu pΩtli do tablicy zauwa┐ymy odmienno╢µ dzia│ania tych pΩtli.

Pocz▒wszy od wersji 5 JavaScript, mo┐liwe jest u┐ywanie takiej pΩtli jak for...in. PΩtla wykonuje polecenia dla ka┐dej w│asno╢ci obiektu lub elementu tablicy. Sk│adnia:

for (warto╢µ in [ obiekt | tablica ])
 {
  ... polecenie;
  ... polecenie;
 }

Nale┐y pamiΩtaµ, ┐e nie ma mo┐liwo╢ci ustalenia kierunku wykonywania siΩ pΩtli. Warto╢µ jest zmieniana zawsze na nastΩpn▒ (nie mo┐na zmieniµ na poprzedni▒).

Czy zwr≤ci│e╢ uwagΩ na fakt, ┐e w deklaracji pΩtli for u┐y│em nawias≤w kwadratowych przy inicjacji licznika, warunku dzia│ania pΩtli oraz warto╢ci zmiany licznika? Zatem pominiΩcie ich i napisanie czego╢ takiego, nie wywo│a b│Ωdu:

for (;;)
 {
  ... Polecenie;
  ... Polecenie;
 }

Zastanawiasz siΩ jak zachowa siΩ przegl▒darka interpretuj▒ca taki skrypt, a zachowa siΩ dok│adnie tak samo jak podczas wykonywania pΩtli while, w kt≤rej warunek jest ca│y czas prawdziwy (np.: 1==1). Skoro taka pΩtla siΩ nigdy nie ko±czy, to po co jej u┐ywaµ, je╢li ma doprowadziµ do zawieszenia przegl▒darki, tudzie┐ Explorera (Internet Explorer ma zabezpieczenie na tak▒ ewentualno╢µ i pozwala wy│▒czyµ aplet, kt≤ry znacznie spowalnia jego pracΩ).

PΩtlΩ mo┐na przerwaµ za pomoc▒ instrukcji break, poznanej w lekcji o instrukcji switch. Polecenia w pΩtli znajduj▒ce siΩ za break nie zostan▒ wykonane. Przeanalizujmy taki przyk│ad:

for (;;)
 {
  odp = prompt("Podaj has│o dostΩpu:");
  if (odp == "nikomu_nie_znane_has│o") then break;
  alert("Niestety! Poda│e╢ nieprawid│owe has│o.");
 }

Je┐eli u┐ytkownik poda nieprawid│owe has│o, to wy╢wietli mu siΩ na ekranie stosowny komunikat, natomiast po wpisaniu prawid│owego has│a, przegl▒darka przeskoczy do linii znajduj▒cych siΩ zaraz za pΩtl▒, nie wy╢wietlaj▒c komunikatu.

U┐ytecznym poleceniem jest continue, kt≤re umieszczone w pΩtli przekazuje jej dzia│anie na pocz▒tek, zmieniaj▒c licznik zgodnie z deklaracj▒. S│u┐y to zwiΩkszeniu szybko╢ci algorytmu, poniewa┐ polecenia za continue nie s▒ wykonywane. Zobaczmy to na przyk│adzie:

 function przykl_continue()
 {
  for (i = 1; i <= 10; i++)
   {
    if (i % 2 == 0) continue;
    alert("To jest " + i + " przebieg pΩtli");
   }
 }

Co w praktyce wygl▒da nastΩpuj▒co:

Widzimy, ┐e wy╢wietlane s▒ komunikaty dla nieparzystych index≤w pΩtli. Dla parzystych (reszta z dzielenia % przez 2 wynosi zero, zob. [operatory]) index≤w pΩtla przeskakuje na sw≤j pocz▒tek.

Sporadycznie u┐ywa siΩ zamiennie z prawdziwymi pΩtlami tzw. etykietek (od ang. label - etykieta). Dekalruje siΩ je tak:

label:
 {
  ... Polecenie;
  ... Polecenie;
 }

W miejsce "label" wstawiamy tekst identyfikuj▒cy etykietkΩ. Aby teraz zapΩtliµ skrypt oraz wyj╢µ z iteracji, u┐ywa siΩ instrukcji continue i break:

start:
 {
  ... Polecenie;
  ... Polecenie;
  if (warunek wyjscia) break start;
  continue start;
 }

Osobi╢cie bardzo odradzam stosowania etykietek na rzecz innych pΩtli, kt≤re s▒ czytelniejsze, wygodniejsze i trudniej siΩ w nich pogubiµ.

TrochΩ siΩ rozpisa│em, ale pΩtle s▒ tymi elementami jΩzyka JavaScript, kt≤rych bedziesz u┐ywa│ bardzo czΩsto, wiΩc dobrze jest w tym temacie dok│adnie wiedzieµ "co i jak".